/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.bpm.core.model;

import java.util.*;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;

/**
 * XML基础元素（此元素配置从xml中解析得到）
 */
public abstract class BaseElement implements HasExtensionAttributes {

    /**
     * 元素ID
     */
    protected String id;
    /**
     * xml行号
     */
    protected int xmlRowNumber;
    /**
     * xml列号
     */
    protected int xmlColumnNumber;
    /**
     * 扩展元素集合
     */
    protected Map<String, List<ExtensionElement>> extensionElements = new LinkedHashMap<String, List<ExtensionElement>>();
    /**
     * extension attributes could be part of each element
     * 扩展属性集合
     */
    protected Map<String, List<ExtensionAttribute>> attributes = new LinkedHashMap<String, List<ExtensionAttribute>>();

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getXmlRowNumber() {
        return xmlRowNumber;
    }

    public void setXmlRowNumber(int xmlRowNumber) {
        this.xmlRowNumber = xmlRowNumber;
    }

    public int getXmlColumnNumber() {
        return xmlColumnNumber;
    }

    public void setXmlColumnNumber(int xmlColumnNumber) {
        this.xmlColumnNumber = xmlColumnNumber;
    }

    public Map<String, List<ExtensionElement>> getExtensionElements() {
        return extensionElements;
    }

    /**
     * 增加扩展元素
     * @param extensionElement
     */
    public void addExtensionElement(ExtensionElement extensionElement) {
        if (extensionElement != null && isNotEmpty(extensionElement.getName())) {
            extensionElements.computeIfAbsent(extensionElement.getName(), k -> new ArrayList<>());
            this.extensionElements.get(extensionElement.getName()).add(extensionElement);
        }
    }

    public void setExtensionElements(Map<String, List<ExtensionElement>> extensionElements) {
        this.extensionElements = extensionElements;
    }

    @Override
    public Map<String, List<ExtensionAttribute>> getAttributes() {
        return attributes;
    }

    @Override
    public String getAttributeValue(String namespace, String name) {
        return Optional.ofNullable(getAttributes())
                .map(map -> map.get(name))
                .orElse(Collections.emptyList()).stream()
                .filter(e -> this.isNamespaceMatching(namespace, e))
                .findFirst().map(ExtensionAttribute::getValue).orElse(null);
    }

    private boolean isNamespaceMatching(String namespace, ExtensionAttribute attribute) {
        return (namespace == null && attribute.getNamespace() == null) || (namespace != null && namespace.equals(attribute.getNamespace()));
    }

    /**
     * 增加扩展属性
     * @param attribute
     */
    @Override
    public void addAttribute(ExtensionAttribute attribute) {
        if (attribute != null && isNotEmpty(attribute.getName())) {
            attributes.computeIfAbsent(attribute.getName(), key -> new ArrayList<>());
            attributes.get(attribute.getName()).add(attribute);
        }
    }

    @Override
    public void setAttributes(Map<String, List<ExtensionAttribute>> attributes) {
        this.attributes = attributes;
    }

    public void setValues(BaseElement otherElement) {
        setId(otherElement.getId());
        if (otherElement.getExtensionElements() != null && !otherElement.getExtensionElements().isEmpty()) {
            Map<String, List<ExtensionElement>> validExtensionElements = otherElement.getExtensionElements().entrySet()
                    .stream().filter(e -> hasElements(e.getValue()))
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            extensionElements.putAll(validExtensionElements);
        }
        if (otherElement.getAttributes() != null && !otherElement.getAttributes().isEmpty()) {
            Map<String, List<ExtensionAttribute>> validAttributes = otherElement.getAttributes().entrySet().stream()
                    .filter(e -> hasElements(e.getValue()))
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            attributes.putAll(validAttributes);
        }
    }

    private boolean hasElements(List<?> listOfElements) {
        return listOfElements != null && !listOfElements.isEmpty();
    }

    @Override
    public abstract BaseElement clone();
}
